"""
函数介绍
"""

# 1. 函数定义格式： def function_name(**x): do something return object
# 关键字：def；括号内是入参，没有入参时为空括号，函数必须有 return 值，没有值 return None，默认没有写，就是 return None


# 两数相加并返回
def add(x, y):
    return x + y


print(add(1, 3))
print(add('1223', '43434'))
print(add([1, 2, 3], [4, 5, 6]))


# 打印输入的数据
def prints(x):
    print(x)


prints('111')


def prints_2(x):
    print(x)
    return None
    print(x + x)


prints_2('222')


def prints_3(x):
    print(x)
    return
    print(x + x)


prints_3('333')


# 2. 指明参数名称
def indicate(x, y):
    print('x=', x)
    print('y=', y)


# 打印发生改变
indicate(1, 2)
indicate(2, 1)
# 打印未发生改变
indicate(x=1, y=2)
indicate(y=2, x=1)


# 3. 给参数设置默认值，注意：设置默认值的参数要放到最后
def default_setting(x, y=3):
    print('x=', x)
    print('y=', y)


# 使用默认值
default_setting(4)
# 覆盖默认值
default_setting(5, 6)
default_setting(y=7, x=9)
# 多个默认值


def default_settings(x, y, w = 1, z = 3):
    print('x=', x)
    print('y=', y)
    print('w=', w)
    print('z=', z)


default_settings(33, 22)
default_settings(33, 22, 11)
default_settings(33, 22, 11, 0)
default_settings(x=1, w=2, y=3, z=4)

# 4. 返回多个值


def return_values(x, y, z):
    return x, y, z


# 一个变量接收，返回为元组
values = return_values(1, 3, 5)
print(values)
# 和 返回值个数相同的变量个数，一一对应接收
q, v, f = return_values(2, 4, 6)
print(q)
print(v)
print(f)


# 5. 参数收集。 * 和 ** 的收集效果，放在参数列表末尾
# * 将函数参数除去必要参数后剩下的部分收集成一个元组，允许为0个
def collect(x, *args):
    print('x', x)
    print('args', args)


collect(1)
collect(1, 2)
collect(1, 2, 3)


# ** 将函数参数除去必要参数后剩下的部分收集成一个字典，允许为0个
def collects(x, **kwargs):

    print('x', x)
    print('kw', kwargs)


collects(1)
# 注意参数写法需要是键值对形式
collects(1, a=2)
collects(1, a=2, b= 3)


# * 和 ** 同时使用。这样参数想传多少个就多少个，但是一般不推荐这么用
def all_args(*args, **kwargs):
    print('args', args)
    print('kwargs', kwargs)


all_args()
all_args(1, 2, 3)
all_args(a=1, b=2)
all_args(1, 2, 3, a=1, b=2)

# 6. 函数的参数使用的是参数对象的引用


def foo(a):
    a.append(99)


l = [1, 2, 3, 4]
print(id(l))
foo(l)
print(id(l))
print(l)

# 7. 函数作为参数引用


def func(f):
    # 说明函数也是个对象
    print(f)
    # 类型是函数
    print(type(f))
    # 加上括号，表示执行这个函数
    f()


def bar():
    print('我是内嵌函数')


func(bar)


def opt_seq(func, seq):
    # 调用传入的函数
    return [func(i) for i in seq]


# 计算 -5  - 4 之间 的绝对值
seq = opt_seq(abs, range(-5, 4))
print(seq)
# 计算 30° - 60° 之间 步长为5 的值对应 三角函数 sin 值
import math
seq1 = opt_seq(math.sin, range(30, 60, 5))
print(seq1)
# 数值列表转字符串列表
seq2 = opt_seq(str, [1, 2, 3, 4, 5, 6])
print(seq2)

# 8. 函数的嵌套


def nest():
    def dddd():
        print('我是函数nest中的嵌套函数')
    print('我是函数nest的结束部分')


# 执行可以看到并没有执行到内嵌函数 a
nest()
# 通过在内部执行 内置函数 a 实现 执行


def nest_1():
    # 作用域仅限 nest_1 函数内部
    def ddd():
        print('我是函数nest中的嵌套函数1')
    print('我是函数nest的结束部分1')
    ddd()


nest_1()
# 通过 内部返回 嵌套函数，实现 内置的嵌套函数 可以被外部调用


def nest_2():

    def bc():
        print('我是函数nest中的嵌套函数2')
    print('我是函数nest的结束部分2')
    # 返回 a，这样就能够 扩大作用域。注意：返回的是函数名，而不是函数名加括号，函数名加括号表示的是执行该函数
    return bc


# 执行 nest_2 函数，并不会执行 a 函数，因为没有调用
a = nest_2()
# 可以看到 nest_2 函数返回了一个 a 的函数对象
print(a)
# 执行函数a
a()
# 注意：使用嵌套函数需要知道闭包问题，请先理解 自由变量，局部变量，全局变量概念，后面会介绍

# 9. 全局变量 使用
aa = 1


def f2():
    # 不做改变不会报错
    print(aa)


f2()


def f3():
    # 声明使用全局变量aa，如果不指定，下面的 aa += 1 将会报错
    global aa
    aa += 1
    print(aa)


f3()
f3()
f3()


# 10. 嵌套函数使用函数作用域中的变量


def f4():
    bb = 0

    def abc():
        # 通过 nonlocal 声明 bb 不是函数 a 中的局部变量，而是使用其外部函数的变量
        nonlocal bb
        bb += 1
        print(bb)
        global aa
        aa += 1
        print(aa)
    return abc


f_ = f4()
f_()
f_()
f_()


# 11. 小练习：计算不同维度下的重量
g = 9.8


def get_weight(g):
    def wei(m):
        # 这里使用了外部函数的参数， 和上面所说的作用域不同
        return g * m
    return wei


# 在重力加速度为 9.8 情况下的重量
weight = get_weight(9.8)
print(weight(50))
print(weight(60))
# 在重力加速度为 10 情况下重量
function = get_weight(10)
print(function(50))
print(function(60))
